package com.uw.uwrpc.core.provider;

import com.uw.uwrpc.core.annotation.UWService;
import com.uw.uwrpc.core.api.RegistryCenter;
import com.uw.uwrpc.core.api.TrafficControl;
import com.uw.uwrpc.core.config.UWRpcProperty;
import com.uw.uwrpc.core.meta.InstanceMeta;
import com.uw.uwrpc.core.meta.ProviderMeta;
import com.uw.uwrpc.core.meta.ServiceMeta;
import com.uw.uwrpc.core.util.MethodUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
@Slf4j
@Data
public class ProviderBootstrap implements ApplicationContextAware, SmartInitializingSingleton, EnvironmentAware {

    private ApplicationContext applicationContext;

    private Environment environment;

    private final MultiValueMap<String, ProviderMeta> skeleton = new LinkedMultiValueMap<>();

    private RegistryCenter registryCenter;

    private UWRpcProperty uwRpcProperty;

    private TrafficControl trafficControl;


    @PostConstruct
    public void init() {
        registryCenter = applicationContext.getBean(RegistryCenter.class);
        uwRpcProperty = applicationContext.getBean(UWRpcProperty.class);
        trafficControl = applicationContext.getBean(TrafficControl.class);
        initSkeleton();
    }

    /**
     * 确保 Spring 容器初始化好以后再注册到注册中心中。
     */
    @Override
    @SneakyThrows
    public void afterSingletonsInstantiated() {
        registryCenter.start();
        registerService();
    }

    @PreDestroy
    @SneakyThrows
    public void destroy() {
        unregisterService();
        registryCenter.stop();
    }

    private void initSkeleton() {
        Map<String, Object> services = applicationContext.getBeansWithAnnotation(UWService.class);
        services.forEach((k, serviceImpl) -> {
            log.info(" ===> scan uwservice: " + k);
            doInitSkeleton(serviceImpl);
        });
    }

    private void doInitSkeleton(Object serviceImpl) {
        Class<?>[] interfaces = serviceImpl.getClass().getInterfaces();
        for (Class<?> item : interfaces) {
            for (Method method : item.getMethods()) {
                if (MethodUtil.checkObjectMethod(method)) {
                    return;
                }

                ProviderMeta providerMeta = createProviderMeta(serviceImpl, method);
                skeleton.add(item.getCanonicalName(), providerMeta);
            }
        }
    }

    private void registerService() throws UnknownHostException {
        Integer port = environment.getProperty("server.port", Integer.class);
        String ip = InetAddress.getLocalHost().getHostAddress();
        InstanceMeta instanceMeta = InstanceMeta.builder()
                .scheme("http")
                .ip(ip)
                .port(port)
                .contextPath("uwrpc")
                .parameters(uwRpcProperty.getProvider().getMetas())
                .build();

        skeleton.keySet().forEach(service -> {
            ServiceMeta serviceMeta = ServiceMeta.builder()
                    .appId(uwRpcProperty.getApp().getAppId())
                    .namespace(uwRpcProperty.getApp().getNamespace())
                    .env(uwRpcProperty.getApp().getEnv())
                    .parameters(uwRpcProperty.getProvider().getMetas())
                    .name(service)
                    .build();
            registryCenter.register(serviceMeta, instanceMeta);
        });
    }

    private void unregisterService() throws UnknownHostException {
        Integer port = environment.getProperty("server.port", Integer.class);
        String ip = InetAddress.getLocalHost().getHostAddress();
        InstanceMeta instanceMeta = InstanceMeta.builder()
                .scheme("http")
                .ip(ip)
                .port(port)
                .contextPath("uwrpc")
                .build();
        skeleton.keySet().forEach(service -> {
            ServiceMeta serviceMeta = ServiceMeta.builder()
                    .appId(uwRpcProperty.getApp().getAppId())
                    .namespace(uwRpcProperty.getApp().getNamespace())
                    .env(uwRpcProperty.getApp().getEnv())
                    .name(service)
                    .parameters(uwRpcProperty.getProvider().getMetas())
                    .build();
            registryCenter.unregister(serviceMeta, instanceMeta);
        });
    }

    private static ProviderMeta createProviderMeta(Object v, Method method) {
        return ProviderMeta.builder()
                .method(method)
                .methodSign(MethodUtil.getMethodSign(method))
                .serviceImpl(v)
                .build();
    }

}
